﻿using System;
using System.Collections.Generic;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace Sharp.Interprocess
{
    internal static class SharedCollectionExtensions
    {
        #region Misc Extensions

        internal static HashSet<T> ToHashSet<T>(this IEnumerable<T> collection)
        {
            return new HashSet<T>(collection);
        }

        internal static void WaitOneWithRetries(this Mutex mutex, int millisecondsTimeout,int retriesCount)
        {
            bool acquireSucceded = false;
            for (int retryIndex = 0; retryIndex < retriesCount; retryIndex++)
            {
                if (mutex.WaitOne(millisecondsTimeout))
                {
                    acquireSucceded = true;
                    break;
                }
            }

            if (!acquireSucceded)
            {
                throw new TimeoutException(String.Format("Mutex timed out while trying to acquire exclusive lock. Operation that acquired lock takes too much time.."));
            }
        }

        #endregion

        #region Memory Mapped File Extensions

        internal static TData ReadData<TData>(this MemoryMappedFile memoryMappedFile, int offset)
            where TData : struct
        {
            TData fetchedData;

            int dataSize = Marshal.SizeOf(typeof(TData));

            using (var viewAccessor = memoryMappedFile.CreateViewAccessor(offset, dataSize))
            {
                viewAccessor.Read<TData>(0, out fetchedData);
            }

            return fetchedData;
        }

        internal static void WriteData<TData>(this MemoryMappedFile memoryMappedFile, int offset, TData dataToWrite)
            where TData : struct
        {
            int dataSize = Marshal.SizeOf(typeof(TData));

            using (var viewAccessor = memoryMappedFile.CreateViewAccessor(offset, dataSize))
            {
                viewAccessor.Write<TData>(0, ref dataToWrite);
            }
        }

        internal static TData[] ReadDataArray<TData>(this MemoryMappedFile memoryMappedFile, int offset,int arrayLength)            
            where  TData : struct
        {
            TData[] fetchedData = new TData[arrayLength];
            
            int dataSize = Marshal.SizeOf(typeof(TData));
            using (var viewAccessor = memoryMappedFile.CreateViewAccessor(offset, dataSize * arrayLength))
            {
                viewAccessor.ReadArray<TData>(0, fetchedData, 0, arrayLength);
            }

            return fetchedData;
        }

        internal static void WriteDataArray<TData>(this MemoryMappedFile memoryMappedFile, int offset, TData[] arrayToWrite)
            where TData : struct
        {
            int dataSize = Marshal.SizeOf(typeof(TData));
            using (var viewAccessor = memoryMappedFile.CreateViewAccessor(offset, dataSize * arrayToWrite.Length))
            {
                viewAccessor.WriteArray<TData>(0, arrayToWrite, 0, arrayToWrite.Length);
            }
        }

        internal static IEnumerable<TData> ReadCollection<TData>(this MemoryMappedFile memoryMappedFile, int offset)
            where TData : struct
        {           

            int dataSize = Marshal.SizeOf(typeof(TData));
            int countDataSize = Marshal.SizeOf(typeof(int));
            using (var viewAccessor = memoryMappedFile.CreateViewAccessor(offset, 0,MemoryMappedFileAccess.Read))
            {
                var collectionCount = viewAccessor.ReadInt32(0);
                var fetchedDataArray = new TData[collectionCount];
                viewAccessor.ReadArray<TData>(0, fetchedDataArray, countDataSize, collectionCount);
                
                return fetchedDataArray;
            }         
        }        

        internal static void WriteCollection<TData>(this MemoryMappedFile memoryMappedFile, int offset, IEnumerable<TData> collectionToWrite)
            where TData : struct
        {
            int dataSize = Marshal.SizeOf(typeof(TData));
            int countDataSize = Marshal.SizeOf(typeof(int));
            int collectionCount = collectionToWrite.Count();
            using (var viewAccessor = memoryMappedFile.CreateViewAccessor(offset, dataSize * collectionCount))
            {
                viewAccessor.Write(0, collectionCount);
                viewAccessor.WriteArray(0, collectionToWrite.ToArray(), countDataSize, collectionCount);
            }
        }

        #endregion
    }
}
